library(magrittr)
library(dplyr)
library(ROCR)
library(e1071)
library(randomForest)
# clear variables
closeAllConnections()
rm(list=ls())

# get new variables
creditDF <- read.csv("training.csv", sep = ",")
totalCreditDF <- creditDF
#creditDF <- creditDF[1:10000,]
head(creditDF)
plot(creditDF$RevolvingUtilizationOfUnsecuredLines) 
plot(creditDF$age) 
plot(creditDF$NumberOfTime30.59DaysPastDueNotWorse) 
plot(creditDF$DebtRatio) 
plot(creditDF$MonthlyIncome) 
plot(creditDF$NumberOfOpenCreditLinesAndLoans) 
plot(creditDF$NumberOfTimes90DaysLate) 
plot(creditDF$NumberRealEstateLoansOrLines) 
plot(creditDF$NumberOfTime60.89DaysPastDueNotWorse) 
plot(creditDF$NumberOfDependents) 
summary(creditDF$RevolvingUtilizationOfUnsecuredLines) 
summary(creditDF$age) 
summary(creditDF$NumberOfTime30.59DaysPastDueNotWorse) 
summary(creditDF$DebtRatio) 
summary(creditDF$MonthlyIncome) 
summary(creditDF$NumberOfOpenCreditLinesAndLoans) 
summary(creditDF$NumberOfTimes90DaysLate) 
summary(creditDF$NumberRealEstateLoansOrLines) 
summary(creditDF$NumberOfTime60.89DaysPastDueNotWorse) 
summary(creditDF$NumberOfDependents) 
# Take a sample to work on
creditDF <- totalCreditDF
creditDF <- creditDF[1:10000,]
creditDF <- creditDF %>% mutate(
                                  #1
                                  age = log(age),
                                  #2
                                  revolving.utilization.of.unsecured.lines = RevolvingUtilizationOfUnsecuredLines + 1,
                                  revolving.utilization.of.unsecured.lines = log(revolving.utilization.of.unsecured.lines),
                                  #3
                                  number.of.time.30.59.days.past.due.not.worse = NumberOfTime30.59DaysPastDueNotWorse + 1,
                                  number.of.time.30.59.days.past.due.not.worse = log(number.of.time.30.59.days.past.due.not.worse),
                                  #4
                                  number.of.time.60.89.days.past.due.not.worse = NumberOfTime60.89DaysPastDueNotWorse + 1,
                                  number.of.time.60.89.days.past.due.not.worse = log(number.of.time.60.89.days.past.due.not.worse),
                                  #5
                                  number.of.times.90.days.late = NumberOfTimes90DaysLate + 1,
                                  number.of.times.90.days.late = log(number.of.times.90.days.late),
                                  #6
                                  debt.ratio = DebtRatio + 1,
                                  debt.ratio = log(debt.ratio),
                                  #7
                                  monthly.income = MonthlyIncome,
                                  monthly.income = ifelse(is.na(monthly.income), 0, monthly.income),
                                  monthly.income = monthly.income + 1,
                                  monthly.income = log(monthly.income),
                                  #8
                                  number.of.open.credit.lines.and.loans = NumberOfOpenCreditLinesAndLoans + 1,
                                  number.of.open.credit.lines.and.loans = log(number.of.open.credit.lines.and.loans),
                                  #9
                                  number.of.dependents = NumberOfDependents,
                                  number.of.dependents = ifelse(is.na(number.of.dependents), 0, number.of.dependents),
                                  number.of.dependents = log(number.of.dependents + 1),
                                  #10
                                  number.real.estate.loans.or.lines = NumberRealEstateLoansOrLines + 1,
                                  number.real.estate.loans.or.lines = log(number.real.estate.loans.or.lines),
                                  
                                  #compose attributes
                                  low.age           = ifelse(age < 21, 1, 0),
                                  not.eligible      = ifelse(age > 60, 1, 0),
                                  
                                  no.dependents                     = ifelse(number.of.dependents == 0, 1, 0),
                                  has.dependents                    = ifelse(number.of.dependents > 0, 1, 0),
                                  
                                  years.of.age.per.dependent        = age / (number.of.dependents + 1),
                                  
                                  no.income                         = ifelse(monthly.income == 0, 1, 0),
                                  income.per.person                 = monthly.income / (number.of.dependents + 1),
                                  income.per.age                    = monthly.income / (age + 1),
                                  
                                  debt                                = log(debt.ratio * monthly.income + 1),
                                  debt.over.income                    = debt / (monthly.income + 1),
                                  no.debt                             = ifelse(debt.ratio == 0, 1, 0),
                                  debt.with.no.income                 = ifelse(debt > 0 && monthly.income == 0, 1, 0),
                                  debt.higher.33                      = ifelse(debt.ratio > .33, 1, 0),
                                  debt.higher.5                       = ifelse(debt.ratio > .5, 1, 0),
                                  undefigned.debt                     = ifelse(is.na(debt.ratio), 1, 0),
                                  debt.per.person                     = debt / (number.of.dependents + 1),
                                  debt.per.open.credit.lines          = debt / (number.of.open.credit.lines.and.loans + 1),
                                  debt.per.number.of.realestate.loans = debt / (number.real.estate.loans.or.lines + 1),
                                  
                                  remaining.income         = monthly.income - debt,
                                  
                                  extreme.credit.use       = ifelse(revolving.utilization.of.unsecured.lines > .99, 1, 0),
                                  no.credit.use            = ifelse(revolving.utilization.of.unsecured.lines == 0, 1, 0),
                                  
                                  credit.card.loans               = number.of.open.credit.lines.and.loans - number.real.estate.loans.or.lines,
                                  credit.card.loans.per.person    = credit.card.loans / (number.of.dependents + 1),
                                  
                                  real.estate.loans.per.person           = number.real.estate.loans.or.lines / (number.of.dependents + 1),
                                  revolving.loans.over.real.estate.loans = credit.card.loans / (number.real.estate.loans.or.lines + 1),
                                  
                                  late.minor.over.income           = number.of.time.30.59.days.past.due.not.worse / (monthly.income + 1),
                                  late.minor.over.remaining.income = number.of.time.30.59.days.past.due.not.worse / (remaining.income + 1),
                                  late.minor.over.debt             = number.of.time.30.59.days.past.due.not.worse / (debt + 1),
                                  
                                  late.mid.over.income           = number.of.time.60.89.days.past.due.not.worse / (monthly.income + 1),
                                  late.mid.over.remaining.income = number.of.time.60.89.days.past.due.not.worse / (remaining.income + 1),
                                  late.mid.over.debt             = number.of.time.60.89.days.past.due.not.worse / (debt + 1),
                                  
                                  late.major.over.income           = number.of.times.90.days.late / (monthly.income + 1),
                                  late.major.over.remaining.income = number.of.times.90.days.late / (remaining.income + 1),
                                  late.major.over.debt             = number.of.times.90.days.late / (debt + 1)
                                  
) %>% select(-Id, 
             -RevolvingUtilizationOfUnsecuredLines, 
             -NumberOfTime30.59DaysPastDueNotWorse, 
             -DebtRatio, 
             -MonthlyIncome, 
             -NumberOfOpenCreditLinesAndLoans, 
             -NumberOfTimes90DaysLate, 
             -NumberRealEstateLoansOrLines, 
             -NumberOfTime60.89DaysPastDueNotWorse, 
             -NumberOfDependents)
head(creditDF)
plot(creditDF$age) 

plot(creditDF$monthly.income) 

plot(creditDF$debt.ratio) 

plot(creditDF$number.of.open.credit.lines.and.loans) 

plot(creditDF$number.real.estate.loans.or.lines) 

plot(creditDF$revolving.utilization.of.unsecured.lines) 

plot(creditDF$number.of.time.30.59.days.past.due.not.worse) 

plot(creditDF$number.of.time.60.89.days.past.due.not.worse) 

plot(creditDF$number.of.times.90.days.late) 

plot(creditDF$number.of.dependents) 

summary(creditDF$age) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  3.045   3.714   3.951   3.912   4.127   4.615 
summary(creditDF$monthly.income) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   7.378   8.375   6.754   8.907  12.247 
summary(creditDF$debt.ratio) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.1601  0.3127  1.5153  0.6137 12.0367 
summary(creditDF$number.of.open.credit.lines.and.loans) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   1.792   2.197   2.075   2.485   3.850 
summary(creditDF$number.real.estate.loans.or.lines) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.0000  0.6931  0.5716  1.0986  2.8904 
summary(creditDF$revolving.utilization.of.unsecured.lines) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.00000 0.03066 0.15300 0.26065 0.45219 9.14217 
summary(creditDF$number.of.time.30.59.days.past.due.not.worse) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.0000  0.0000  0.1468  0.0000  4.5951 
summary(creditDF$number.of.time.60.89.days.past.due.not.worse) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.00000 0.00000 0.00000 0.04669 0.00000 4.59512 
summary(creditDF$number.of.times.90.days.late) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.00000 0.00000 0.00000 0.05835 0.00000 4.59512 
summary(creditDF$number.of.dependents) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.0000  0.0000  0.3896  0.6931  3.0445 
# normalization
creditDF <- creditDF %>% mutate(
                                  #1
                                  age                                          = (age - min(age)) / (max(age) - min(age)) ,
                                  revolving.utilization.of.unsecured.lines     = (revolving.utilization.of.unsecured.lines - min(revolving.utilization.of.unsecured.lines)) / (max(revolving.utilization.of.unsecured.lines) - min(revolving.utilization.of.unsecured.lines)) ,
                                  number.of.time.30.59.days.past.due.not.worse = (number.of.time.30.59.days.past.due.not.worse - min(number.of.time.30.59.days.past.due.not.worse)) / (max(number.of.time.30.59.days.past.due.not.worse) - min(number.of.time.30.59.days.past.due.not.worse)),
                                  number.of.time.60.89.days.past.due.not.worse = (number.of.time.60.89.days.past.due.not.worse - min(number.of.time.60.89.days.past.due.not.worse)) / (max(number.of.time.60.89.days.past.due.not.worse) - min(number.of.time.60.89.days.past.due.not.worse)),
                                  number.of.times.90.days.late                 = (number.of.times.90.days.late - min(number.of.times.90.days.late)) / (max(number.of.times.90.days.late) - min(number.of.times.90.days.late)),
                                  debt.ratio                                   = (debt.ratio - min(debt.ratio)) / (max(debt.ratio) - min(debt.ratio)),
                                  monthly.income                               = (monthly.income - min(monthly.income)) / (max(monthly.income) - min(monthly.income)),
                                  number.of.open.credit.lines.and.loans        = (number.of.open.credit.lines.and.loans - min(number.of.open.credit.lines.and.loans)) / (max(number.of.open.credit.lines.and.loans) - min(number.of.open.credit.lines.and.loans)),
                                  number.of.dependents                         = (number.of.dependents - min(number.of.dependents)) / (max(number.of.dependents) - min(number.of.dependents)),
                                  number.real.estate.loans.or.lines            = (number.real.estate.loans.or.lines - min(number.real.estate.loans.or.lines)) / (max(number.real.estate.loans.or.lines) - min(number.real.estate.loans.or.lines)),
                                  
                                  #compose attributes
                                  years.of.age.per.dependent             = (years.of.age.per.dependent - min(years.of.age.per.dependent)) / (max(years.of.age.per.dependent) - min(years.of.age.per.dependent)),
                                  income.per.person                      = (income.per.person - min(income.per.person)) / (max(income.per.person) - min(income.per.person)),
                                  income.per.age                         = (income.per.age - min(income.per.age)) / (max(income.per.age) - min(income.per.age)),
                                  debt                                   = (debt - min(debt)) / (max(debt) - min(debt)),
                                  debt.over.income                       = (debt.over.income - min(debt.over.income)) / (max(debt.over.income) - min(debt.over.income)),
                                  debt.per.person                        = (debt.per.person - min(debt.per.person)) / (max(debt.per.person) - min(debt.per.person)),
                                  debt.per.open.credit.lines             = (debt.per.open.credit.lines - min(debt.per.open.credit.lines)) / (max(debt.per.open.credit.lines) - min(debt.per.open.credit.lines)),
                                  debt.per.number.of.realestate.loans    = (debt.per.number.of.realestate.loans - min(debt.per.number.of.realestate.loans)) / (max(debt.per.number.of.realestate.loans) - min(debt.per.number.of.realestate.loans)),
                                  remaining.income                       = (remaining.income - min(remaining.income)) / (max(remaining.income) - min(remaining.income)),
                                  credit.card.loans                      = (credit.card.loans - min(credit.card.loans)) / (max(credit.card.loans) - min(credit.card.loans)),
                                  credit.card.loans.per.person           = (credit.card.loans.per.person - min(credit.card.loans.per.person)) / (max(credit.card.loans.per.person) - min(credit.card.loans.per.person)),
                                  real.estate.loans.per.person           = (real.estate.loans.per.person - min(real.estate.loans.per.person)) / (max(real.estate.loans.per.person) - min(real.estate.loans.per.person)),
                                  revolving.loans.over.real.estate.loans = (revolving.loans.over.real.estate.loans - min(revolving.loans.over.real.estate.loans)) / (max(revolving.loans.over.real.estate.loans) - min(revolving.loans.over.real.estate.loans)),
                                  late.minor.over.income                 = (late.minor.over.income - min(late.minor.over.income)) / (max(late.minor.over.income) - min(late.minor.over.income)),
                                  late.minor.over.remaining.income       = (late.minor.over.remaining.income - min(late.minor.over.remaining.income)) / (max(late.minor.over.remaining.income) - min(late.minor.over.remaining.income)),
                                  late.minor.over.debt                   = (late.minor.over.debt - min(late.minor.over.debt)) / (max(late.minor.over.debt) - min(late.minor.over.debt)),
                                  late.mid.over.income                   = (late.mid.over.income - min(late.mid.over.income)) / (max(late.mid.over.income) - min(late.mid.over.income)),
                                  late.mid.over.remaining.income         = (late.mid.over.remaining.income - min(late.mid.over.remaining.income)) / (max(late.mid.over.remaining.income) - min(late.mid.over.remaining.income)),
                                  late.mid.over.debt                     = (late.mid.over.debt - min(late.mid.over.debt)) / (max(late.mid.over.debt) - min(late.mid.over.debt)),
                                  late.major.over.income                 = (late.major.over.income - min(late.major.over.income)) / (max(late.major.over.income) - min(late.major.over.income)),
                                  late.major.over.remaining.income       = (late.major.over.remaining.income - min(late.major.over.remaining.income)) / (max(late.major.over.remaining.income) - min(late.major.over.remaining.income)),
                                  late.major.over.debt                   = (late.major.over.debt - min(late.major.over.debt)) / (max(late.major.over.debt) - min(late.major.over.debt))
                                
)
head(creditDF)
plot(creditDF$age) 

plot(creditDF$monthly.income) 

plot(creditDF$debt.ratio) 

plot(creditDF$number.of.open.credit.lines.and.loans) 

plot(creditDF$number.real.estate.loans.or.lines) 

plot(creditDF$revolving.utilization.of.unsecured.lines) 

plot(creditDF$number.of.time.30.59.days.past.due.not.worse) 

plot(creditDF$number.of.time.60.89.days.past.due.not.worse) 

plot(creditDF$number.of.times.90.days.late) 

plot(creditDF$number.of.dependents) 

summary(creditDF$age) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.4260  0.5773  0.5522  0.6893  1.0000 
summary(creditDF$monthly.income) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.6025  0.6839  0.5515  0.7273  1.0000 
summary(creditDF$debt.ratio) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.00000 0.01330 0.02598 0.12589 0.05099 1.00000 
summary(creditDF$number.of.open.credit.lines.and.loans) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.4654  0.5707  0.5390  0.6454  1.0000 
summary(creditDF$number.real.estate.loans.or.lines) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.0000  0.2398  0.1978  0.3801  1.0000 
summary(creditDF$revolving.utilization.of.unsecured.lines) 
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
0.000000 0.003354 0.016736 0.028510 0.049462 1.000000 
summary(creditDF$number.of.time.30.59.days.past.due.not.worse) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.00000 0.00000 0.00000 0.03195 0.00000 1.00000 
summary(creditDF$number.of.time.60.89.days.past.due.not.worse) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.00000 0.00000 0.00000 0.01016 0.00000 1.00000 
summary(creditDF$number.of.times.90.days.late) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.0000  0.0000  0.0127  0.0000  1.0000 
summary(creditDF$number.of.dependents) 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.0000  0.0000  0.1280  0.2277  1.0000 
d <- dim(creditDF)
trainingDF <- creditDF[1:(d[1] * .8), ]
testDF     <- creditDF[(d[1] * .8 + 1):d[1],]
head(trainingDF)
head(testDF)
model.lr <- glm(SeriousDlqin2yrs ~.,  family=binomial(link='logit'),  data=trainingDF)
pred.lr <- predict(model.lr, newdata=testDF, type="response")
prediction from a rank-deficient fit may be misleading
pr.lr <- prediction(pred.lr, testDF$SeriousDlqin2yrs)
prf <- performance(pr.lr, measure = "tpr", x.measure = "fpr")
plot(prf)

auc <- performance(pr.lr, measure = "auc")
auc <- auc@y.values[[1]]
auc
[1] 0.8099722
model.svm <- svm(SeriousDlqin2yrs ~ ., data=trainingDF)
Variable(s) ‘low.age’ and ‘not.eligible’ and ‘debt.with.no.income’ and ‘undefigned.debt’ constant. Cannot scale data.
pred.svm <- predict(model.svm, testDF, type="response")
pr.svm <- prediction(pred.svm, testDF$SeriousDlqin2yrs)
prf.svm <- performance(pr.svm, measure = "tpr", x.measure = "fpr")
plot(prf.svm)

auc <- performance(pr.svm, measure = "auc")
auc <- auc@y.values[[1]]
auc
[1] 0.6960223
auc
[1] 0.8099621
training.results.DF <- data.frame(lr = predict(model.lr, trainingDF, type="response"),
                                  #svm = predict(model.svm, trainingDF, type="response"),
                                  rf = predict(model.rf, trainingDF, type="response"),
                                  SeriousDlqin2yrs = trainingDF$SeriousDlqin2yrs)
prediction from a rank-deficient fit may be misleading
model.lr.fin <- glm(SeriousDlqin2yrs ~.,  family=binomial(link='logit'),  data=training.results.DF)
glm.fit: fitted probabilities numerically 0 or 1 occurred
test.fin.df <- data.frame(lr = pred.lr, 
                          #svm = pred.svm, 
                          rf = pred.rf, 
                          SeriousDlqin2yrs = testDF$SeriousDlqin2yrs)
pred.lr.fin <- predict(model.lr.fin, newdata=test.fin.df, type="response")
pr.lr.fin <- prediction(pred.lr.fin, test.fin.df$SeriousDlqin2yrs)
prf.fin <- performance(pr.lr.fin, measure = "tpr", x.measure = "fpr")
plot(prf.fin)

auc <- performance(pr.lr.fin, measure = "auc")
auc <- auc@y.values[[1]]
auc
[1] 0.8083532
LS0tCnRpdGxlOiAiR2l2ZSBtZSBjcmVkaXQgbm90ZWJvb2siCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KYGBge3J9CmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoUk9DUikKbGlicmFyeShlMTA3MSkKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmBgYAoKYGBge3J9CiMgY2xlYXIgdmFyaWFibGVzCmNsb3NlQWxsQ29ubmVjdGlvbnMoKQpybShsaXN0PWxzKCkpCgojIGdldCBuZXcgdmFyaWFibGVzCmNyZWRpdERGIDwtIHJlYWQuY3N2KCJ0cmFpbmluZy5jc3YiLCBzZXAgPSAiLCIpCnRvdGFsQ3JlZGl0REYgPC0gY3JlZGl0REYKI2NyZWRpdERGIDwtIGNyZWRpdERGWzE6MTAwMDAsXQpoZWFkKGNyZWRpdERGKQpgYGAKCgpgYGB7cn0KcGxvdChjcmVkaXRERiRSZXZvbHZpbmdVdGlsaXphdGlvbk9mVW5zZWN1cmVkTGluZXMpIApgYGAKYGBge3J9CnBsb3QoY3JlZGl0REYkYWdlKSAKYGBgCgpgYGB7cn0KcGxvdChjcmVkaXRERiROdW1iZXJPZlRpbWUzMC41OURheXNQYXN0RHVlTm90V29yc2UpIApgYGAKYGBge3J9CnBsb3QoY3JlZGl0REYkRGVidFJhdGlvKSAKYGBgCgpgYGB7cn0KcGxvdChjcmVkaXRERiRNb250aGx5SW5jb21lKSAKYGBgCgpgYGB7cn0KcGxvdChjcmVkaXRERiROdW1iZXJPZk9wZW5DcmVkaXRMaW5lc0FuZExvYW5zKSAKYGBgCgpgYGB7cn0KcGxvdChjcmVkaXRERiROdW1iZXJPZlRpbWVzOTBEYXlzTGF0ZSkgCmBgYAoKYGBge3J9CnBsb3QoY3JlZGl0REYkTnVtYmVyUmVhbEVzdGF0ZUxvYW5zT3JMaW5lcykgCmBgYApgYGB7cn0KcGxvdChjcmVkaXRERiROdW1iZXJPZlRpbWU2MC44OURheXNQYXN0RHVlTm90V29yc2UpIApgYGAKYGBge3J9CnBsb3QoY3JlZGl0REYkTnVtYmVyT2ZEZXBlbmRlbnRzKSAKYGBgCmBgYHtyfQpzdW1tYXJ5KGNyZWRpdERGJFJldm9sdmluZ1V0aWxpemF0aW9uT2ZVbnNlY3VyZWRMaW5lcykgCmBgYApgYGB7cn0Kc3VtbWFyeShjcmVkaXRERiRhZ2UpIApgYGAKYGBge3J9CnN1bW1hcnkoY3JlZGl0REYkTnVtYmVyT2ZUaW1lMzAuNTlEYXlzUGFzdER1ZU5vdFdvcnNlKSAKYGBgCmBgYHtyfQpzdW1tYXJ5KGNyZWRpdERGJERlYnRSYXRpbykgCmBgYApgYGB7cn0Kc3VtbWFyeShjcmVkaXRERiRNb250aGx5SW5jb21lKSAKYGBgCmBgYHtyfQpzdW1tYXJ5KGNyZWRpdERGJE51bWJlck9mT3BlbkNyZWRpdExpbmVzQW5kTG9hbnMpIApgYGAKYGBge3J9CnN1bW1hcnkoY3JlZGl0REYkTnVtYmVyT2ZUaW1lczkwRGF5c0xhdGUpIApgYGAKYGBge3J9CnN1bW1hcnkoY3JlZGl0REYkTnVtYmVyUmVhbEVzdGF0ZUxvYW5zT3JMaW5lcykgCmBgYApgYGB7cn0Kc3VtbWFyeShjcmVkaXRERiROdW1iZXJPZlRpbWU2MC44OURheXNQYXN0RHVlTm90V29yc2UpIApgYGAKYGBge3J9CnN1bW1hcnkoY3JlZGl0REYkTnVtYmVyT2ZEZXBlbmRlbnRzKSAKYGBgCgoKYGBge3J9CiMgVGFrZSBhIHNhbXBsZSB0byB3b3JrIG9uCmNyZWRpdERGIDwtIHRvdGFsQ3JlZGl0REYKY3JlZGl0REYgPC0gY3JlZGl0REZbMToxMDAwMCxdCmBgYAoKCgpgYGB7cn0KY3JlZGl0REYgPC0gY3JlZGl0REYgJT4lIG11dGF0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZ2UgPSBsb2coYWdlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXZvbHZpbmcudXRpbGl6YXRpb24ub2YudW5zZWN1cmVkLmxpbmVzID0gUmV2b2x2aW5nVXRpbGl6YXRpb25PZlVuc2VjdXJlZExpbmVzICsgMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldm9sdmluZy51dGlsaXphdGlvbi5vZi51bnNlY3VyZWQubGluZXMgPSBsb2cocmV2b2x2aW5nLnV0aWxpemF0aW9uLm9mLnVuc2VjdXJlZC5saW5lcyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjMwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyLm9mLnRpbWUuMzAuNTkuZGF5cy5wYXN0LmR1ZS5ub3Qud29yc2UgPSBOdW1iZXJPZlRpbWUzMC41OURheXNQYXN0RHVlTm90V29yc2UgKyAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyLm9mLnRpbWUuMzAuNTkuZGF5cy5wYXN0LmR1ZS5ub3Qud29yc2UgPSBsb2cobnVtYmVyLm9mLnRpbWUuMzAuNTkuZGF5cy5wYXN0LmR1ZS5ub3Qud29yc2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIzQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlci5vZi50aW1lLjYwLjg5LmRheXMucGFzdC5kdWUubm90LndvcnNlID0gTnVtYmVyT2ZUaW1lNjAuODlEYXlzUGFzdER1ZU5vdFdvcnNlICsgMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlci5vZi50aW1lLjYwLjg5LmRheXMucGFzdC5kdWUubm90LndvcnNlID0gbG9nKG51bWJlci5vZi50aW1lLjYwLjg5LmRheXMucGFzdC5kdWUubm90LndvcnNlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICM1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1iZXIub2YudGltZXMuOTAuZGF5cy5sYXRlID0gTnVtYmVyT2ZUaW1lczkwRGF5c0xhdGUgKyAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyLm9mLnRpbWVzLjkwLmRheXMubGF0ZSA9IGxvZyhudW1iZXIub2YudGltZXMuOTAuZGF5cy5sYXRlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICM2CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWJ0LnJhdGlvID0gRGVidFJhdGlvICsgMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlYnQucmF0aW8gPSBsb2coZGVidC5yYXRpbyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjNwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9udGhseS5pbmNvbWUgPSBNb250aGx5SW5jb21lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9udGhseS5pbmNvbWUgPSBpZmVsc2UoaXMubmEobW9udGhseS5pbmNvbWUpLCAwLCBtb250aGx5LmluY29tZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb250aGx5LmluY29tZSA9IG1vbnRobHkuaW5jb21lICsgMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vbnRobHkuaW5jb21lID0gbG9nKG1vbnRobHkuaW5jb21lKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICM4CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1iZXIub2Yub3Blbi5jcmVkaXQubGluZXMuYW5kLmxvYW5zID0gTnVtYmVyT2ZPcGVuQ3JlZGl0TGluZXNBbmRMb2FucyArIDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1iZXIub2Yub3Blbi5jcmVkaXQubGluZXMuYW5kLmxvYW5zID0gbG9nKG51bWJlci5vZi5vcGVuLmNyZWRpdC5saW5lcy5hbmQubG9hbnMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIzkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlci5vZi5kZXBlbmRlbnRzID0gTnVtYmVyT2ZEZXBlbmRlbnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyLm9mLmRlcGVuZGVudHMgPSBpZmVsc2UoaXMubmEobnVtYmVyLm9mLmRlcGVuZGVudHMpLCAwLCBudW1iZXIub2YuZGVwZW5kZW50cyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1iZXIub2YuZGVwZW5kZW50cyA9IGxvZyhudW1iZXIub2YuZGVwZW5kZW50cyArIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIzEwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1iZXIucmVhbC5lc3RhdGUubG9hbnMub3IubGluZXMgPSBOdW1iZXJSZWFsRXN0YXRlTG9hbnNPckxpbmVzICsgMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlci5yZWFsLmVzdGF0ZS5sb2Fucy5vci5saW5lcyA9IGxvZyhudW1iZXIucmVhbC5lc3RhdGUubG9hbnMub3IubGluZXMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjY29tcG9zZSBhdHRyaWJ1dGVzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb3cuYWdlICAgICAgICAgICA9IGlmZWxzZShhZ2UgPCAyMSwgMSwgMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3QuZWxpZ2libGUgICAgICA9IGlmZWxzZShhZ2UgPiA2MCwgMSwgMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vLmRlcGVuZGVudHMgICAgICAgICAgICAgICAgICAgICA9IGlmZWxzZShudW1iZXIub2YuZGVwZW5kZW50cyA9PSAwLCAxLCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhhcy5kZXBlbmRlbnRzICAgICAgICAgICAgICAgICAgICA9IGlmZWxzZShudW1iZXIub2YuZGVwZW5kZW50cyA+IDAsIDEsIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFycy5vZi5hZ2UucGVyLmRlcGVuZGVudCAgICAgICAgPSBhZ2UgLyAobnVtYmVyLm9mLmRlcGVuZGVudHMgKyAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm8uaW5jb21lICAgICAgICAgICAgICAgICAgICAgICAgID0gaWZlbHNlKG1vbnRobHkuaW5jb21lID09IDAsIDEsIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jb21lLnBlci5wZXJzb24gICAgICAgICAgICAgICAgID0gbW9udGhseS5pbmNvbWUgLyAobnVtYmVyLm9mLmRlcGVuZGVudHMgKyAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY29tZS5wZXIuYWdlICAgICAgICAgICAgICAgICAgICA9IG1vbnRobHkuaW5jb21lIC8gKGFnZSArIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWJ0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA9IGxvZyhkZWJ0LnJhdGlvICogbW9udGhseS5pbmNvbWUgKyAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlYnQub3Zlci5pbmNvbWUgICAgICAgICAgICAgICAgICAgID0gZGVidCAvIChtb250aGx5LmluY29tZSArIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm8uZGVidCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPSBpZmVsc2UoZGVidC5yYXRpbyA9PSAwLCAxLCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlYnQud2l0aC5uby5pbmNvbWUgICAgICAgICAgICAgICAgID0gaWZlbHNlKGRlYnQgPiAwICYmIG1vbnRobHkuaW5jb21lID09IDAsIDEsIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVidC5oaWdoZXIuMzMgICAgICAgICAgICAgICAgICAgICAgPSBpZmVsc2UoZGVidC5yYXRpbyA+IC4zMywgMSwgMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWJ0LmhpZ2hlci41ICAgICAgICAgICAgICAgICAgICAgICA9IGlmZWxzZShkZWJ0LnJhdGlvID4gLjUsIDEsIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5kZWZpZ25lZC5kZWJ0ICAgICAgICAgICAgICAgICAgICAgPSBpZmVsc2UoaXMubmEoZGVidC5yYXRpbyksIDEsIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVidC5wZXIucGVyc29uICAgICAgICAgICAgICAgICAgICAgPSBkZWJ0IC8gKG51bWJlci5vZi5kZXBlbmRlbnRzICsgMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWJ0LnBlci5vcGVuLmNyZWRpdC5saW5lcyAgICAgICAgICA9IGRlYnQgLyAobnVtYmVyLm9mLm9wZW4uY3JlZGl0LmxpbmVzLmFuZC5sb2FucyArIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVidC5wZXIubnVtYmVyLm9mLnJlYWxlc3RhdGUubG9hbnMgPSBkZWJ0IC8gKG51bWJlci5yZWFsLmVzdGF0ZS5sb2Fucy5vci5saW5lcyArIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZW1haW5pbmcuaW5jb21lICAgICAgICAgPSBtb250aGx5LmluY29tZSAtIGRlYnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4dHJlbWUuY3JlZGl0LnVzZSAgICAgICA9IGlmZWxzZShyZXZvbHZpbmcudXRpbGl6YXRpb24ub2YudW5zZWN1cmVkLmxpbmVzID4gLjk5LCAxLCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vLmNyZWRpdC51c2UgICAgICAgICAgICA9IGlmZWxzZShyZXZvbHZpbmcudXRpbGl6YXRpb24ub2YudW5zZWN1cmVkLmxpbmVzID09IDAsIDEsIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcmVkaXQuY2FyZC5sb2FucyAgICAgICAgICAgICAgID0gbnVtYmVyLm9mLm9wZW4uY3JlZGl0LmxpbmVzLmFuZC5sb2FucyAtIG51bWJlci5yZWFsLmVzdGF0ZS5sb2Fucy5vci5saW5lcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNyZWRpdC5jYXJkLmxvYW5zLnBlci5wZXJzb24gICAgPSBjcmVkaXQuY2FyZC5sb2FucyAvIChudW1iZXIub2YuZGVwZW5kZW50cyArIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFsLmVzdGF0ZS5sb2Fucy5wZXIucGVyc29uICAgICAgICAgICA9IG51bWJlci5yZWFsLmVzdGF0ZS5sb2Fucy5vci5saW5lcyAvIChudW1iZXIub2YuZGVwZW5kZW50cyArIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV2b2x2aW5nLmxvYW5zLm92ZXIucmVhbC5lc3RhdGUubG9hbnMgPSBjcmVkaXQuY2FyZC5sb2FucyAvIChudW1iZXIucmVhbC5lc3RhdGUubG9hbnMub3IubGluZXMgKyAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0ZS5taW5vci5vdmVyLmluY29tZSAgICAgICAgICAgPSBudW1iZXIub2YudGltZS4zMC41OS5kYXlzLnBhc3QuZHVlLm5vdC53b3JzZSAvIChtb250aGx5LmluY29tZSArIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0ZS5taW5vci5vdmVyLnJlbWFpbmluZy5pbmNvbWUgPSBudW1iZXIub2YudGltZS4zMC41OS5kYXlzLnBhc3QuZHVlLm5vdC53b3JzZSAvIChyZW1haW5pbmcuaW5jb21lICsgMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXRlLm1pbm9yLm92ZXIuZGVidCAgICAgICAgICAgICA9IG51bWJlci5vZi50aW1lLjMwLjU5LmRheXMucGFzdC5kdWUubm90LndvcnNlIC8gKGRlYnQgKyAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0ZS5taWQub3Zlci5pbmNvbWUgICAgICAgICAgID0gbnVtYmVyLm9mLnRpbWUuNjAuODkuZGF5cy5wYXN0LmR1ZS5ub3Qud29yc2UgLyAobW9udGhseS5pbmNvbWUgKyAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhdGUubWlkLm92ZXIucmVtYWluaW5nLmluY29tZSA9IG51bWJlci5vZi50aW1lLjYwLjg5LmRheXMucGFzdC5kdWUubm90LndvcnNlIC8gKHJlbWFpbmluZy5pbmNvbWUgKyAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhdGUubWlkLm92ZXIuZGVidCAgICAgICAgICAgICA9IG51bWJlci5vZi50aW1lLjYwLjg5LmRheXMucGFzdC5kdWUubm90LndvcnNlIC8gKGRlYnQgKyAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0ZS5tYWpvci5vdmVyLmluY29tZSAgICAgICAgICAgPSBudW1iZXIub2YudGltZXMuOTAuZGF5cy5sYXRlIC8gKG1vbnRobHkuaW5jb21lICsgMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXRlLm1ham9yLm92ZXIucmVtYWluaW5nLmluY29tZSA9IG51bWJlci5vZi50aW1lcy45MC5kYXlzLmxhdGUgLyAocmVtYWluaW5nLmluY29tZSArIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0ZS5tYWpvci5vdmVyLmRlYnQgICAgICAgICAgICAgPSBudW1iZXIub2YudGltZXMuOTAuZGF5cy5sYXRlIC8gKGRlYnQgKyAxKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCikgJT4lIHNlbGVjdCgtSWQsIAogICAgICAgICAgICAgLVJldm9sdmluZ1V0aWxpemF0aW9uT2ZVbnNlY3VyZWRMaW5lcywgCiAgICAgICAgICAgICAtTnVtYmVyT2ZUaW1lMzAuNTlEYXlzUGFzdER1ZU5vdFdvcnNlLCAKICAgICAgICAgICAgIC1EZWJ0UmF0aW8sIAogICAgICAgICAgICAgLU1vbnRobHlJbmNvbWUsIAogICAgICAgICAgICAgLU51bWJlck9mT3BlbkNyZWRpdExpbmVzQW5kTG9hbnMsIAogICAgICAgICAgICAgLU51bWJlck9mVGltZXM5MERheXNMYXRlLCAKICAgICAgICAgICAgIC1OdW1iZXJSZWFsRXN0YXRlTG9hbnNPckxpbmVzLCAKICAgICAgICAgICAgIC1OdW1iZXJPZlRpbWU2MC44OURheXNQYXN0RHVlTm90V29yc2UsIAogICAgICAgICAgICAgLU51bWJlck9mRGVwZW5kZW50cykKCmhlYWQoY3JlZGl0REYpCmBgYAoKCgoKYGBge3J9CnBsb3QoY3JlZGl0REYkYWdlKSAKCnBsb3QoY3JlZGl0REYkbW9udGhseS5pbmNvbWUpIApwbG90KGNyZWRpdERGJGRlYnQucmF0aW8pIAoKcGxvdChjcmVkaXRERiRudW1iZXIub2Yub3Blbi5jcmVkaXQubGluZXMuYW5kLmxvYW5zKSAKcGxvdChjcmVkaXRERiRudW1iZXIucmVhbC5lc3RhdGUubG9hbnMub3IubGluZXMpIApwbG90KGNyZWRpdERGJHJldm9sdmluZy51dGlsaXphdGlvbi5vZi51bnNlY3VyZWQubGluZXMpIAoKcGxvdChjcmVkaXRERiRudW1iZXIub2YudGltZS4zMC41OS5kYXlzLnBhc3QuZHVlLm5vdC53b3JzZSkgCnBsb3QoY3JlZGl0REYkbnVtYmVyLm9mLnRpbWUuNjAuODkuZGF5cy5wYXN0LmR1ZS5ub3Qud29yc2UpIApwbG90KGNyZWRpdERGJG51bWJlci5vZi50aW1lcy45MC5kYXlzLmxhdGUpIAoKcGxvdChjcmVkaXRERiRudW1iZXIub2YuZGVwZW5kZW50cykgCmBgYAoKCmBgYHtyfQoKc3VtbWFyeShjcmVkaXRERiRhZ2UpIApzdW1tYXJ5KGNyZWRpdERGJG1vbnRobHkuaW5jb21lKSAKc3VtbWFyeShjcmVkaXRERiRkZWJ0LnJhdGlvKSAKc3VtbWFyeShjcmVkaXRERiRudW1iZXIub2Yub3Blbi5jcmVkaXQubGluZXMuYW5kLmxvYW5zKSAKc3VtbWFyeShjcmVkaXRERiRudW1iZXIucmVhbC5lc3RhdGUubG9hbnMub3IubGluZXMpIApzdW1tYXJ5KGNyZWRpdERGJHJldm9sdmluZy51dGlsaXphdGlvbi5vZi51bnNlY3VyZWQubGluZXMpIApzdW1tYXJ5KGNyZWRpdERGJG51bWJlci5vZi50aW1lLjMwLjU5LmRheXMucGFzdC5kdWUubm90LndvcnNlKSAKc3VtbWFyeShjcmVkaXRERiRudW1iZXIub2YudGltZS42MC44OS5kYXlzLnBhc3QuZHVlLm5vdC53b3JzZSkgCnN1bW1hcnkoY3JlZGl0REYkbnVtYmVyLm9mLnRpbWVzLjkwLmRheXMubGF0ZSkgCnN1bW1hcnkoY3JlZGl0REYkbnVtYmVyLm9mLmRlcGVuZGVudHMpIApgYGAKCmBgYHtyfQojIG5vcm1hbGl6YXRpb24KCmNyZWRpdERGIDwtIGNyZWRpdERGICU+JSBtdXRhdGUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjMQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWdlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPSAoYWdlIC0gbWluKGFnZSkpIC8gKG1heChhZ2UpIC0gbWluKGFnZSkpICwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldm9sdmluZy51dGlsaXphdGlvbi5vZi51bnNlY3VyZWQubGluZXMgICAgID0gKHJldm9sdmluZy51dGlsaXphdGlvbi5vZi51bnNlY3VyZWQubGluZXMgLSBtaW4ocmV2b2x2aW5nLnV0aWxpemF0aW9uLm9mLnVuc2VjdXJlZC5saW5lcykpIC8gKG1heChyZXZvbHZpbmcudXRpbGl6YXRpb24ub2YudW5zZWN1cmVkLmxpbmVzKSAtIG1pbihyZXZvbHZpbmcudXRpbGl6YXRpb24ub2YudW5zZWN1cmVkLmxpbmVzKSkgLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyLm9mLnRpbWUuMzAuNTkuZGF5cy5wYXN0LmR1ZS5ub3Qud29yc2UgPSAobnVtYmVyLm9mLnRpbWUuMzAuNTkuZGF5cy5wYXN0LmR1ZS5ub3Qud29yc2UgLSBtaW4obnVtYmVyLm9mLnRpbWUuMzAuNTkuZGF5cy5wYXN0LmR1ZS5ub3Qud29yc2UpKSAvIChtYXgobnVtYmVyLm9mLnRpbWUuMzAuNTkuZGF5cy5wYXN0LmR1ZS5ub3Qud29yc2UpIC0gbWluKG51bWJlci5vZi50aW1lLjMwLjU5LmRheXMucGFzdC5kdWUubm90LndvcnNlKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1iZXIub2YudGltZS42MC44OS5kYXlzLnBhc3QuZHVlLm5vdC53b3JzZSA9IChudW1iZXIub2YudGltZS42MC44OS5kYXlzLnBhc3QuZHVlLm5vdC53b3JzZSAtIG1pbihudW1iZXIub2YudGltZS42MC44OS5kYXlzLnBhc3QuZHVlLm5vdC53b3JzZSkpIC8gKG1heChudW1iZXIub2YudGltZS42MC44OS5kYXlzLnBhc3QuZHVlLm5vdC53b3JzZSkgLSBtaW4obnVtYmVyLm9mLnRpbWUuNjAuODkuZGF5cy5wYXN0LmR1ZS5ub3Qud29yc2UpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlci5vZi50aW1lcy45MC5kYXlzLmxhdGUgICAgICAgICAgICAgICAgID0gKG51bWJlci5vZi50aW1lcy45MC5kYXlzLmxhdGUgLSBtaW4obnVtYmVyLm9mLnRpbWVzLjkwLmRheXMubGF0ZSkpIC8gKG1heChudW1iZXIub2YudGltZXMuOTAuZGF5cy5sYXRlKSAtIG1pbihudW1iZXIub2YudGltZXMuOTAuZGF5cy5sYXRlKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWJ0LnJhdGlvICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA9IChkZWJ0LnJhdGlvIC0gbWluKGRlYnQucmF0aW8pKSAvIChtYXgoZGVidC5yYXRpbykgLSBtaW4oZGVidC5yYXRpbykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9udGhseS5pbmNvbWUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPSAobW9udGhseS5pbmNvbWUgLSBtaW4obW9udGhseS5pbmNvbWUpKSAvIChtYXgobW9udGhseS5pbmNvbWUpIC0gbWluKG1vbnRobHkuaW5jb21lKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1iZXIub2Yub3Blbi5jcmVkaXQubGluZXMuYW5kLmxvYW5zICAgICAgICA9IChudW1iZXIub2Yub3Blbi5jcmVkaXQubGluZXMuYW5kLmxvYW5zIC0gbWluKG51bWJlci5vZi5vcGVuLmNyZWRpdC5saW5lcy5hbmQubG9hbnMpKSAvIChtYXgobnVtYmVyLm9mLm9wZW4uY3JlZGl0LmxpbmVzLmFuZC5sb2FucykgLSBtaW4obnVtYmVyLm9mLm9wZW4uY3JlZGl0LmxpbmVzLmFuZC5sb2FucykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyLm9mLmRlcGVuZGVudHMgICAgICAgICAgICAgICAgICAgICAgICAgPSAobnVtYmVyLm9mLmRlcGVuZGVudHMgLSBtaW4obnVtYmVyLm9mLmRlcGVuZGVudHMpKSAvIChtYXgobnVtYmVyLm9mLmRlcGVuZGVudHMpIC0gbWluKG51bWJlci5vZi5kZXBlbmRlbnRzKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1iZXIucmVhbC5lc3RhdGUubG9hbnMub3IubGluZXMgICAgICAgICAgICA9IChudW1iZXIucmVhbC5lc3RhdGUubG9hbnMub3IubGluZXMgLSBtaW4obnVtYmVyLnJlYWwuZXN0YXRlLmxvYW5zLm9yLmxpbmVzKSkgLyAobWF4KG51bWJlci5yZWFsLmVzdGF0ZS5sb2Fucy5vci5saW5lcykgLSBtaW4obnVtYmVyLnJlYWwuZXN0YXRlLmxvYW5zLm9yLmxpbmVzKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICNjb21wb3NlIGF0dHJpYnV0ZXMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXJzLm9mLmFnZS5wZXIuZGVwZW5kZW50ICAgICAgICAgICAgID0gKHllYXJzLm9mLmFnZS5wZXIuZGVwZW5kZW50IC0gbWluKHllYXJzLm9mLmFnZS5wZXIuZGVwZW5kZW50KSkgLyAobWF4KHllYXJzLm9mLmFnZS5wZXIuZGVwZW5kZW50KSAtIG1pbih5ZWFycy5vZi5hZ2UucGVyLmRlcGVuZGVudCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jb21lLnBlci5wZXJzb24gICAgICAgICAgICAgICAgICAgICAgPSAoaW5jb21lLnBlci5wZXJzb24gLSBtaW4oaW5jb21lLnBlci5wZXJzb24pKSAvIChtYXgoaW5jb21lLnBlci5wZXJzb24pIC0gbWluKGluY29tZS5wZXIucGVyc29uKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNvbWUucGVyLmFnZSAgICAgICAgICAgICAgICAgICAgICAgICA9IChpbmNvbWUucGVyLmFnZSAtIG1pbihpbmNvbWUucGVyLmFnZSkpIC8gKG1heChpbmNvbWUucGVyLmFnZSkgLSBtaW4oaW5jb21lLnBlci5hZ2UpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlYnQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID0gKGRlYnQgLSBtaW4oZGVidCkpIC8gKG1heChkZWJ0KSAtIG1pbihkZWJ0KSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWJ0Lm92ZXIuaW5jb21lICAgICAgICAgICAgICAgICAgICAgICA9IChkZWJ0Lm92ZXIuaW5jb21lIC0gbWluKGRlYnQub3Zlci5pbmNvbWUpKSAvIChtYXgoZGVidC5vdmVyLmluY29tZSkgLSBtaW4oZGVidC5vdmVyLmluY29tZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVidC5wZXIucGVyc29uICAgICAgICAgICAgICAgICAgICAgICAgPSAoZGVidC5wZXIucGVyc29uIC0gbWluKGRlYnQucGVyLnBlcnNvbikpIC8gKG1heChkZWJ0LnBlci5wZXJzb24pIC0gbWluKGRlYnQucGVyLnBlcnNvbikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVidC5wZXIub3Blbi5jcmVkaXQubGluZXMgICAgICAgICAgICAgPSAoZGVidC5wZXIub3Blbi5jcmVkaXQubGluZXMgLSBtaW4oZGVidC5wZXIub3Blbi5jcmVkaXQubGluZXMpKSAvIChtYXgoZGVidC5wZXIub3Blbi5jcmVkaXQubGluZXMpIC0gbWluKGRlYnQucGVyLm9wZW4uY3JlZGl0LmxpbmVzKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWJ0LnBlci5udW1iZXIub2YucmVhbGVzdGF0ZS5sb2FucyAgICA9IChkZWJ0LnBlci5udW1iZXIub2YucmVhbGVzdGF0ZS5sb2FucyAtIG1pbihkZWJ0LnBlci5udW1iZXIub2YucmVhbGVzdGF0ZS5sb2FucykpIC8gKG1heChkZWJ0LnBlci5udW1iZXIub2YucmVhbGVzdGF0ZS5sb2FucykgLSBtaW4oZGVidC5wZXIubnVtYmVyLm9mLnJlYWxlc3RhdGUubG9hbnMpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbWFpbmluZy5pbmNvbWUgICAgICAgICAgICAgICAgICAgICAgID0gKHJlbWFpbmluZy5pbmNvbWUgLSBtaW4ocmVtYWluaW5nLmluY29tZSkpIC8gKG1heChyZW1haW5pbmcuaW5jb21lKSAtIG1pbihyZW1haW5pbmcuaW5jb21lKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcmVkaXQuY2FyZC5sb2FucyAgICAgICAgICAgICAgICAgICAgICA9IChjcmVkaXQuY2FyZC5sb2FucyAtIG1pbihjcmVkaXQuY2FyZC5sb2FucykpIC8gKG1heChjcmVkaXQuY2FyZC5sb2FucykgLSBtaW4oY3JlZGl0LmNhcmQubG9hbnMpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNyZWRpdC5jYXJkLmxvYW5zLnBlci5wZXJzb24gICAgICAgICAgID0gKGNyZWRpdC5jYXJkLmxvYW5zLnBlci5wZXJzb24gLSBtaW4oY3JlZGl0LmNhcmQubG9hbnMucGVyLnBlcnNvbikpIC8gKG1heChjcmVkaXQuY2FyZC5sb2Fucy5wZXIucGVyc29uKSAtIG1pbihjcmVkaXQuY2FyZC5sb2Fucy5wZXIucGVyc29uKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFsLmVzdGF0ZS5sb2Fucy5wZXIucGVyc29uICAgICAgICAgICA9IChyZWFsLmVzdGF0ZS5sb2Fucy5wZXIucGVyc29uIC0gbWluKHJlYWwuZXN0YXRlLmxvYW5zLnBlci5wZXJzb24pKSAvIChtYXgocmVhbC5lc3RhdGUubG9hbnMucGVyLnBlcnNvbikgLSBtaW4ocmVhbC5lc3RhdGUubG9hbnMucGVyLnBlcnNvbikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV2b2x2aW5nLmxvYW5zLm92ZXIucmVhbC5lc3RhdGUubG9hbnMgPSAocmV2b2x2aW5nLmxvYW5zLm92ZXIucmVhbC5lc3RhdGUubG9hbnMgLSBtaW4ocmV2b2x2aW5nLmxvYW5zLm92ZXIucmVhbC5lc3RhdGUubG9hbnMpKSAvIChtYXgocmV2b2x2aW5nLmxvYW5zLm92ZXIucmVhbC5lc3RhdGUubG9hbnMpIC0gbWluKHJldm9sdmluZy5sb2Fucy5vdmVyLnJlYWwuZXN0YXRlLmxvYW5zKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXRlLm1pbm9yLm92ZXIuaW5jb21lICAgICAgICAgICAgICAgICA9IChsYXRlLm1pbm9yLm92ZXIuaW5jb21lIC0gbWluKGxhdGUubWlub3Iub3Zlci5pbmNvbWUpKSAvIChtYXgobGF0ZS5taW5vci5vdmVyLmluY29tZSkgLSBtaW4obGF0ZS5taW5vci5vdmVyLmluY29tZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0ZS5taW5vci5vdmVyLnJlbWFpbmluZy5pbmNvbWUgICAgICAgPSAobGF0ZS5taW5vci5vdmVyLnJlbWFpbmluZy5pbmNvbWUgLSBtaW4obGF0ZS5taW5vci5vdmVyLnJlbWFpbmluZy5pbmNvbWUpKSAvIChtYXgobGF0ZS5taW5vci5vdmVyLnJlbWFpbmluZy5pbmNvbWUpIC0gbWluKGxhdGUubWlub3Iub3Zlci5yZW1haW5pbmcuaW5jb21lKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXRlLm1pbm9yLm92ZXIuZGVidCAgICAgICAgICAgICAgICAgICA9IChsYXRlLm1pbm9yLm92ZXIuZGVidCAtIG1pbihsYXRlLm1pbm9yLm92ZXIuZGVidCkpIC8gKG1heChsYXRlLm1pbm9yLm92ZXIuZGVidCkgLSBtaW4obGF0ZS5taW5vci5vdmVyLmRlYnQpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhdGUubWlkLm92ZXIuaW5jb21lICAgICAgICAgICAgICAgICAgID0gKGxhdGUubWlkLm92ZXIuaW5jb21lIC0gbWluKGxhdGUubWlkLm92ZXIuaW5jb21lKSkgLyAobWF4KGxhdGUubWlkLm92ZXIuaW5jb21lKSAtIG1pbihsYXRlLm1pZC5vdmVyLmluY29tZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0ZS5taWQub3Zlci5yZW1haW5pbmcuaW5jb21lICAgICAgICAgPSAobGF0ZS5taWQub3Zlci5yZW1haW5pbmcuaW5jb21lIC0gbWluKGxhdGUubWlkLm92ZXIucmVtYWluaW5nLmluY29tZSkpIC8gKG1heChsYXRlLm1pZC5vdmVyLnJlbWFpbmluZy5pbmNvbWUpIC0gbWluKGxhdGUubWlkLm92ZXIucmVtYWluaW5nLmluY29tZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0ZS5taWQub3Zlci5kZWJ0ICAgICAgICAgICAgICAgICAgICAgPSAobGF0ZS5taWQub3Zlci5kZWJ0IC0gbWluKGxhdGUubWlkLm92ZXIuZGVidCkpIC8gKG1heChsYXRlLm1pZC5vdmVyLmRlYnQpIC0gbWluKGxhdGUubWlkLm92ZXIuZGVidCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0ZS5tYWpvci5vdmVyLmluY29tZSAgICAgICAgICAgICAgICAgPSAobGF0ZS5tYWpvci5vdmVyLmluY29tZSAtIG1pbihsYXRlLm1ham9yLm92ZXIuaW5jb21lKSkgLyAobWF4KGxhdGUubWFqb3Iub3Zlci5pbmNvbWUpIC0gbWluKGxhdGUubWFqb3Iub3Zlci5pbmNvbWUpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhdGUubWFqb3Iub3Zlci5yZW1haW5pbmcuaW5jb21lICAgICAgID0gKGxhdGUubWFqb3Iub3Zlci5yZW1haW5pbmcuaW5jb21lIC0gbWluKGxhdGUubWFqb3Iub3Zlci5yZW1haW5pbmcuaW5jb21lKSkgLyAobWF4KGxhdGUubWFqb3Iub3Zlci5yZW1haW5pbmcuaW5jb21lKSAtIG1pbihsYXRlLm1ham9yLm92ZXIucmVtYWluaW5nLmluY29tZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0ZS5tYWpvci5vdmVyLmRlYnQgICAgICAgICAgICAgICAgICAgPSAobGF0ZS5tYWpvci5vdmVyLmRlYnQgLSBtaW4obGF0ZS5tYWpvci5vdmVyLmRlYnQpKSAvIChtYXgobGF0ZS5tYWpvci5vdmVyLmRlYnQpIC0gbWluKGxhdGUubWFqb3Iub3Zlci5kZWJ0KSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKKQoKaGVhZChjcmVkaXRERikKYGBgCgoKCgoKYGBge3J9CnBsb3QoY3JlZGl0REYkYWdlKSAKCnBsb3QoY3JlZGl0REYkbW9udGhseS5pbmNvbWUpIApwbG90KGNyZWRpdERGJGRlYnQucmF0aW8pIAoKcGxvdChjcmVkaXRERiRudW1iZXIub2Yub3Blbi5jcmVkaXQubGluZXMuYW5kLmxvYW5zKSAKcGxvdChjcmVkaXRERiRudW1iZXIucmVhbC5lc3RhdGUubG9hbnMub3IubGluZXMpIApwbG90KGNyZWRpdERGJHJldm9sdmluZy51dGlsaXphdGlvbi5vZi51bnNlY3VyZWQubGluZXMpIAoKcGxvdChjcmVkaXRERiRudW1iZXIub2YudGltZS4zMC41OS5kYXlzLnBhc3QuZHVlLm5vdC53b3JzZSkgCnBsb3QoY3JlZGl0REYkbnVtYmVyLm9mLnRpbWUuNjAuODkuZGF5cy5wYXN0LmR1ZS5ub3Qud29yc2UpIApwbG90KGNyZWRpdERGJG51bWJlci5vZi50aW1lcy45MC5kYXlzLmxhdGUpIAoKcGxvdChjcmVkaXRERiRudW1iZXIub2YuZGVwZW5kZW50cykgCmBgYAoKCmBgYHtyfQoKc3VtbWFyeShjcmVkaXRERiRhZ2UpIApzdW1tYXJ5KGNyZWRpdERGJG1vbnRobHkuaW5jb21lKSAKc3VtbWFyeShjcmVkaXRERiRkZWJ0LnJhdGlvKSAKc3VtbWFyeShjcmVkaXRERiRudW1iZXIub2Yub3Blbi5jcmVkaXQubGluZXMuYW5kLmxvYW5zKSAKc3VtbWFyeShjcmVkaXRERiRudW1iZXIucmVhbC5lc3RhdGUubG9hbnMub3IubGluZXMpIApzdW1tYXJ5KGNyZWRpdERGJHJldm9sdmluZy51dGlsaXphdGlvbi5vZi51bnNlY3VyZWQubGluZXMpIApzdW1tYXJ5KGNyZWRpdERGJG51bWJlci5vZi50aW1lLjMwLjU5LmRheXMucGFzdC5kdWUubm90LndvcnNlKSAKc3VtbWFyeShjcmVkaXRERiRudW1iZXIub2YudGltZS42MC44OS5kYXlzLnBhc3QuZHVlLm5vdC53b3JzZSkgCnN1bW1hcnkoY3JlZGl0REYkbnVtYmVyLm9mLnRpbWVzLjkwLmRheXMubGF0ZSkgCnN1bW1hcnkoY3JlZGl0REYkbnVtYmVyLm9mLmRlcGVuZGVudHMpIApgYGAKCmBgYHtyfQpkIDwtIGRpbShjcmVkaXRERikKdHJhaW5pbmdERiA8LSBjcmVkaXRERlsxOihkWzFdICogLjgpLCBdCnRlc3RERiAgICAgPC0gY3JlZGl0REZbKGRbMV0gKiAuOCArIDEpOmRbMV0sXQoKaGVhZCh0cmFpbmluZ0RGKQpoZWFkKHRlc3RERikKYGBgCgpgYGB7cn0KbW9kZWwubHIgPC0gZ2xtKFNlcmlvdXNEbHFpbjJ5cnMgfi4sICBmYW1pbHk9Ymlub21pYWwobGluaz0nbG9naXQnKSwgIGRhdGE9dHJhaW5pbmdERikKCnByZWQubHIgPC0gcHJlZGljdChtb2RlbC5sciwgbmV3ZGF0YT10ZXN0REYsIHR5cGU9InJlc3BvbnNlIikKcHIubHIgPC0gcHJlZGljdGlvbihwcmVkLmxyLCB0ZXN0REYkU2VyaW91c0RscWluMnlycykKcHJmIDwtIHBlcmZvcm1hbmNlKHByLmxyLCBtZWFzdXJlID0gInRwciIsIHgubWVhc3VyZSA9ICJmcHIiKQpwbG90KHByZikKCmF1YyA8LSBwZXJmb3JtYW5jZShwci5sciwgbWVhc3VyZSA9ICJhdWMiKQphdWMgPC0gYXVjQHkudmFsdWVzW1sxXV0KYXVjCmBgYAoKYGBge3J9Cm1vZGVsLnN2bSA8LSBzdm0oU2VyaW91c0RscWluMnlycyB+IC4sIGRhdGE9dHJhaW5pbmdERikKCnByZWQuc3ZtIDwtIHByZWRpY3QobW9kZWwuc3ZtLCB0ZXN0REYsIHR5cGU9InJlc3BvbnNlIikKcHIuc3ZtIDwtIHByZWRpY3Rpb24ocHJlZC5zdm0sIHRlc3RERiRTZXJpb3VzRGxxaW4yeXJzKQpwcmYuc3ZtIDwtIHBlcmZvcm1hbmNlKHByLnN2bSwgbWVhc3VyZSA9ICJ0cHIiLCB4Lm1lYXN1cmUgPSAiZnByIikKcGxvdChwcmYuc3ZtKQoKYXVjIDwtIHBlcmZvcm1hbmNlKHByLnN2bSwgbWVhc3VyZSA9ICJhdWMiKQphdWMgPC0gYXVjQHkudmFsdWVzW1sxXV0KYXVjCgpgYGAKCmBgYHtyfQptb2RlbC5yZiA8LSByYW5kb21Gb3Jlc3QoU2VyaW91c0RscWluMnlycyB+IC4sIHRyYWluaW5nREYsIG50cmVlPTUwMCwgbm9ybS52b3Rlcz1GQUxTRSkgCgpwcmVkLnJmIDwtIHByZWRpY3QobW9kZWwucmYsIHRlc3RERiwgdHlwZT0icmVzcG9uc2UiKQpwci5yZiA8LSBwcmVkaWN0aW9uKHByZWQucmYsIHRlc3RERiRTZXJpb3VzRGxxaW4yeXJzKQpwcmYucmYgPC0gcGVyZm9ybWFuY2UocHIucmYsIG1lYXN1cmUgPSAidHByIiwgeC5tZWFzdXJlID0gImZwciIpCnBsb3QocHJmLnJmKQoKYXVjIDwtIHBlcmZvcm1hbmNlKHByLnJmLCBtZWFzdXJlID0gImF1YyIpCmF1YyA8LSBhdWNAeS52YWx1ZXNbWzFdXQphdWMKYGBgCgpgYGB7cn0KdHJhaW5pbmcucmVzdWx0cy5ERiA8LSBkYXRhLmZyYW1lKGxyID0gcHJlZGljdChtb2RlbC5sciwgdHJhaW5pbmdERiwgdHlwZT0icmVzcG9uc2UiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICNzdm0gPSBwcmVkaWN0KG1vZGVsLnN2bSwgdHJhaW5pbmdERiwgdHlwZT0icmVzcG9uc2UiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJmID0gcHJlZGljdChtb2RlbC5yZiwgdHJhaW5pbmdERiwgdHlwZT0icmVzcG9uc2UiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNlcmlvdXNEbHFpbjJ5cnMgPSB0cmFpbmluZ0RGJFNlcmlvdXNEbHFpbjJ5cnMpCgpgYGAKYGBge3J9Cm1vZGVsLmxyLmZpbiA8LSBnbG0oU2VyaW91c0RscWluMnlycyB+LiwgIGZhbWlseT1iaW5vbWlhbChsaW5rPSdsb2dpdCcpLCAgZGF0YT10cmFpbmluZy5yZXN1bHRzLkRGKQoKdGVzdC5maW4uZGYgPC0gZGF0YS5mcmFtZShsciA9IHByZWQubHIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICNzdm0gPSBwcmVkLnN2bSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmYgPSBwcmVkLnJmLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBTZXJpb3VzRGxxaW4yeXJzID0gdGVzdERGJFNlcmlvdXNEbHFpbjJ5cnMpCgpwcmVkLmxyLmZpbiA8LSBwcmVkaWN0KG1vZGVsLmxyLmZpbiwgbmV3ZGF0YT10ZXN0LmZpbi5kZiwgdHlwZT0icmVzcG9uc2UiKQpwci5sci5maW4gPC0gcHJlZGljdGlvbihwcmVkLmxyLmZpbiwgdGVzdC5maW4uZGYkU2VyaW91c0RscWluMnlycykKcHJmLmZpbiA8LSBwZXJmb3JtYW5jZShwci5sci5maW4sIG1lYXN1cmUgPSAidHByIiwgeC5tZWFzdXJlID0gImZwciIpCnBsb3QocHJmLmZpbikKCmF1YyA8LSBwZXJmb3JtYW5jZShwci5sci5maW4sIG1lYXN1cmUgPSAiYXVjIikKYXVjIDwtIGF1Y0B5LnZhbHVlc1tbMV1dCmF1YwpgYGAKCg==